///////////////////////////////////////////////////////////////////////////////////////////////////
// Illusion Ray Development Kit (IDK)           Copyright (c) 2010 ( dimitriy.martynenko@gmail.com )
///////////////////////////////////////////////////////////////////////////////////////////////////
// Created : 2010-05-22
// Updated : ...
// Licence : NEW BSD
// Info	   : "SCL" Safe Code Lib (for C++ unmanaged)
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

namespace Platform
{
	//
	// TRefCounter MacOs Impl
	//
	//NOTE : Win32 will be use Interlocked...Acquire when perfomance is critical?
	template<>
	inline void TRefCounter<Config::ThreadSafe>::AddRef(void)
	{
		return __sync_add_and_fetch (&mRefCount, 1);
	}

	template<>
	inline bool TRefCounter<Config::ThreadSafe>::Release()
	{
		return __sync_add_and_fetch (&mRefCount, -1) == 0;
	}

        inline float Time::Get(void)
        {
                Win32::LARGE_INTEGER freq;
                Win32::DWORD_PTR oldmask = Win32::SetThreadAffinityMask(Win32::GetCurrentThread(), 0);
                Win32::QueryPerformanceFrequency(&freq);
                Win32::LONGLONG UpdatesPerMillisecond = freq.QuadPart / 1000;

                Win32::LARGE_INTEGER counter;
                Win32::QueryPerformanceCounter(&counter);
                Win32::SetThreadAffinityMask(Win32::GetCurrentThread(), oldmask);

                return(static_cast<float>(counter.QuadPart / UpdatesPerMillisecond));
        }

        inline void Time::Sleep(uint ms)
        {
                Win32::Sleep(ms);
        }

        //
        // OutputConsole Win32 Impl
        //
        inline void OutputConsole::Message(const char* text)
        {
                Platform::Win32::OutputDebugStringA(text);
        }

        //
        // FileHandle
        //
        class FileHandle
        {
        private:
                FileHandle(const FileHandle&);
                FileHandle& operator=(const FileHandle&);
        private:
                Win32::HANDLE mHandle;
        public:
                FileHandle()
                        : mHandle(INVALID_HANDLE_VALUE)
                {
                }

                bool IsValid() const
                {
                        return mHandle != INVALID_HANDLE_VALUE;
                }

                bool Open(const char* path, bool create)
                {
                        const dword FILE_ATTRIB_NORMAL = 0x0000080;
                        //
                        const dword GENERIC_R = 0x80000000 ;
                        const dword GENERIC_W = 0x40000000; // TODO: in mode?
                        const dword GENERIC = (create) ? GENERIC_W : GENERIC_R;

                        const dword OPEN_EXISTING = 3;
                        const dword CREATE_ALWAYS = 2;
                        const dword MODE = (create) ? CREATE_ALWAYS : OPEN_EXISTING;

                        mHandle = Win32::CreateFileA(path, GENERIC, 0, 0, MODE, FILE_ATTRIB_NORMAL, 0);

                        return mHandle != INVALID_HANDLE_VALUE;
                }

                void Close()
                {
                        if(IsValid())
                        {
                                Win32::CloseHandle(mHandle);
                        }

                        mHandle = INVALID_HANDLE_VALUE;
                }

                uint GetSize() const
                {
                        return Win32::GetFileSize(mHandle, 0);
                }

                void SetPosition(uint pos)
                {
                        Win32::SetFilePointer(mHandle, pos, 0, 0);
                }

                void Seek(uint pos)
                {
                        Win32::SetFilePointer(mHandle, pos, 0, 1);
                }

                template<typename T>
                bool Read(T data, uint size, dword& bread)
                {
                        return Win32::ReadFile(mHandle, data, size, &bread, 0) != FALSE;
                }
        };

        //
        // TRefCounter Win32 Impl
        //
        //NOTE : Win32 will be use Interlocked...Acquire when perfomance is critical?

        template<>
        inline void TRefCounter<Config::ThreadSafe>::AddRef(void)
        {
                Win32::InterlockedIncrement(&mRefCount);
        }

        template<>
        inline bool TRefCounter<Config::ThreadSafe>::Release()
        {
                return Win32::InterlockedDecrement(&mRefCount) == 0;
        }

        //
        // TThreadLocker Win32 Impl
        //
        class Locker
        {
        private:
                Locker(const Locker&);
                Locker& operator=(const Locker&);
        private:
                volatile long mThreadID;
        public:
                inline Locker() : mThreadID(0) {}

                volatile inline bool TryLock(void)
                {
                        return Win32::InterlockedCompareExchange(&mThreadID, Win32::GetCurrentThreadId(), 0) != 0;
                }

                volatile inline void UnLock(void)
                {
                        Win32::InterlockedExchange(&mThreadID, 0);
                }
        };

        //
        // TMutex Win32 Impl
        //
        template<>
        class TMutex<Config::ThreadSafe>
        {
        private:
                Locker& mLocker;
        private:
                TMutex(const TMutex&);
                TMutex& operator=(const TMutex&);
        private:
                inline void Enter()
                {
                        for(int i = 0; mLocker.TryLock(); ++i)
                        {
                                if(i == Platform::Config::ThreadLockMaxTimeIterationToSleep)
                                {
                                        i = 0;
                                        Win32::Sleep(Platform::Config::ThreadLockSleepTimeInMs);
                                }
                        }
                }

                inline void Leave()
                {
                        mLocker.UnLock();
                }
        public:
                inline TMutex(Locker& locker) : mLocker(locker)
                {
                        Enter();
                }

                ~TMutex()
                {
                        Leave();
                }
        };

        //
        // Event Win32 Impl
        //
        class Event
        {
        public:
                Win32::HANDLE mHandle;
        private:
                Event(const Event&);
                Event& operator=(const Event&);
        public:
                Event(bool autoReset)
                {
                        mHandle = Win32::CreateEventA(0, autoReset, 0, 0);
                }

                ~Event()
                {
                        Win32::CloseHandle(mHandle);
                }

                void Set()
                {
                        Win32::SetEvent(mHandle);
                }

                void Reset()
                {
                        Win32::ResetEvent(mHandle);
                }
        };

        //
        // TEventCondition Win32 Impl
        //

        class EventCondition
        {
        private:
                EventCondition(const EventCondition&);
                EventCondition& operator=(const EventCondition&);
        public:
                typedef unsigned int EventID;

                EventCondition(){}

                bool Wait(Event& event1)
                {
                        return WAIT_OBJECT_0 != Win32::WaitForSingleObject(event1.mHandle, INFINITE);
                }

                EventID Wait(Event& event1, Event& event2)
                {
                        Win32::HANDLE waitEvents[2] = {event1.mHandle, event2.mHandle};
                        return Win32::WaitForMultipleObjects(2, waitEvents, 0, INFINITE);
                }
        };

        //
        // Thread Win32 Impl
        //

        class ThreadHandle
        {
        private:
                ThreadHandle(const ThreadHandle&);
                ThreadHandle& operator=(const ThreadHandle&);
        private:
                Win32::HANDLE mHandle;
                Win32::DWORD mThreadID;
        private:
                template<typename Derived, void (Derived::*action)()>
                static Win32::DWORD WINAPI __ThreadCall(void* p)
                {
                        if(!p) return 0;

                        ((*(Derived*)p).*action)();

                        return 0;
                }
        public:
                ThreadHandle(){}

                template<typename Derived, void (Derived::*action)()>
                void Create(Derived* caller)
                {
                        mHandle = Win32::CreateThread(Null, 0, __ThreadCall<Derived, action>, caller, 0, &mThreadID);
                }

                void Close()
                {
                        Win32::CloseHandle(mHandle);
                }
        };


        template<typename T>
        inline T Math<T>::Sqrt(T x)
        {
        __asm
        {
            fld x // r0 = x
            fsqrt // r0 = sqrtf( r0 )
        }// returns r0
        }

        template<typename T>
        inline T Math<T>::Sin(T x)
        {
        T r = (T)0;

        __asm
        {
            fld x
            fsin
            fstp r
        }

        return r;
        }

        template<typename T>
        inline T Math<T>::Cos(T x)
        {
                T r = (T)0;

        __asm
        {
            fld x
            fcos
            fstp r
        }

        return r;
        }

        template<typename T>
        inline T Math<T>::ArcTan2(T y, T x)
        {
            __asm
            {
                fld y
                fld x
                fpatan // r0 = atan2(y/x)
            } // returns r0
        }
}
